﻿//  -------------------- iSharePoint -------------------- //
// 
//  Company:	IT Complex, LLC
//  Project:	Smart.Common
//  File name:	XmlFormsBuilder.cs
//  Developer:	Solomatov Igor
//  Created:	15.04.2012
// 
//  -------------------- iSharePoint -------------------- //

using System;
using System.Collections.Generic;
using System.IO;
using System.Web.UI.WebControls;
using Smart.Common.Rules;

namespace Smart.Common.Forms
{
	/// <summary>
	/// Xml form builder
	/// </summary>
	public abstract class XmlFormsBuilder : FormsBuilder
	{
		/// <summary>
		/// Xml form builder
		/// </summary>
		protected XmlFormsBuilder(FormsContext ctx)
			: base(ctx)
		{
		}

		/// <summary>
		/// Parse source (create new form using builder)
		/// </summary>
		public override MetaForms ParseFormsDefinition(object source)
		{
			if (source == null)
				throw new ArgumentNullException("source");

			var xmlSource = source as string;

			if (xmlSource == null)
				throw new ArgumentException("source");

			var forms = xmlSource.DeserializeXml<Forms>();

			return ParseForms(forms);
		}

		/// <summary>
		/// Parse forms
		/// </summary>
		public virtual MetaForms ParseForms(Forms forms)
		{
			var mfs = CreateForms();

			for (var i = 0; forms.Items != null && i < forms.Items.Length; i++)
			{
				var item = forms.Items[i];

				if (item is Form)
				{
					var mf = ParseForm((Form)item);

					mfs.Forms.Add(mf);
				}
				else if (item is ChooseRules)
				{
					var crs = ParseChooseRules((ChooseRules)item);

					mfs.Rules.AddRange(crs);
				}
				else
				{
					var mctrl = ParseXml(item);

					if (mctrl == null)
						throw new NotSupportedException("ParseForms {" + item.GetType().FullName + "}");

					if (mctrl is MetaFormsRule || mctrl.GetType().IsSubclassOf(typeof(MetaFormsRule)))
						mfs.Rules.Add((MetaFormsRule)mctrl);
					else if (mctrl is MetaForm || mctrl.GetType().IsSubclassOf(typeof(MetaForm)))
						mfs.Forms.Add((MetaForm)mctrl);

					throw new NotSupportedException("ParseForms {" + mctrl.GetType().FullName + "}");
				}
			}

			mfs.SetParent();

			return mfs;
		}

		/// <summary>
		/// Parse choose rules
		/// </summary>
		public virtual List<MetaFormsRule> ParseChooseRules(ChooseRules rules)
		{
			var mrs = new List<MetaFormsRule>();

			var rulesService = GetFormsRulesService();

			var rulesSource = rulesService.Source;

			for (var i = 0; rules.Items != null && i < rules.Items.Length; i++)
			{
				var rule = rules.Items[i];

				var cr = CreateChooseRule(rule.Condition, rule.FormName);

				cr.Rule = new XmlRule(rulesSource.ParseCondition(cr.Condition), rulesSource.ParseAction(cr.FormName));

				mrs.Add(cr);
			}

			return mrs;
		}

		/// <summary>
		/// Parse form
		/// </summary>
		public virtual MetaForm ParseForm(Form form)
		{
			var mf = CreateForm(form.Title, form.Name);

			for (var i = 0; form.Items != null && i < form.Items.Length; i++)
			{
				var item = form.Items[i];

				if (item is Tabs)
				{
					var mts = ParseTabs((Tabs)item);

					mf.Items.Add(mts);
				}
				else if (item is ActionRules)
				{
					var ars = ParseActionRules((ActionRules)item);

					mf.Rules.AddRange(ars);
				}
				else if (item is ButtonTabs)
				{
					mf.ButtonTabs = ParseButtonTabs((ButtonTabs)item);
				}
				else if (item is Group)
				{
					var mg = ParseGroup((Group)item);

					mf.Items.Add(mg);
				}
				else if (item is Control)
				{
					var cc = ParseControl((Control)item);

					mf.Items.Add(cc);
				}
				else if (item is Table)
				{
					var mt = ParseTable((Table)item);

					mf.Items.Add(mt);
				}
				else if (item is Scripts)
				{
					var ss = ParseScripts((Scripts)item);

					mf.Items.Add(ss);
				}
				else
				{
					var mctrl = ParseXml(item);

					if (mctrl == null)
						throw new NotSupportedException("ParseForm {" + item.GetType().FullName + "}");

					mf.Items.Add(mctrl);
				}
			}

			mf.SetParent();

			return mf;
		}

		/// <summary>
		/// Parse action rules
		/// </summary>
		public virtual List<MetaFormRule> ParseActionRules(ActionRules rules)
		{
			var mrs = new List<MetaFormRule>();

			var rulesService = Ctx.Service.Formatter.GetFormRulesService();
			var rulesSource = rulesService.Source;

			for (var i = 0; rules.Items != null && i < rules.Items.Length; i++)
			{
				var rule = rules.Items[i];

				var ar = CreateActionRule(rule.Condition, rule.Action);

				ar.Rule = new XmlRule(rulesSource.ParseCondition(ar.Condition), rulesSource.ParseAction(ar.Action));

				mrs.Add(ar);
			}

			return mrs;
		}

		/// <summary>
		/// Parse buttons tabs
		/// </summary>
		public virtual MetaFormButtonTabs ParseButtonTabs(ButtonTabs buttonTabs)
		{
			var bts = CreateButtonTabs();

			var tabs = buttonTabs.Items;

			for (int I = 0, ICnt = tabs == null ? 0 : tabs.Length; I < ICnt; I++)
			{
				var tab = tabs[I];

				var bt = ParseButtonTab(tab);

				bts.Items.Add(bt);
			}

			bts.SetParent();

			return bts;
		}

		/// <summary>
		/// Parse buttons tab
		/// </summary>
		public virtual MetaFormButtonTab ParseButtonTab(ButtonTab buttonTab)
		{
			var bt = CreateButtonTab(buttonTab.Title, buttonTab.Name, buttonTab.Initial);

			bt.SetSecurity(buttonTab.Security);
			bt.Condition = buttonTab.Condition;

			var groups = buttonTab.Items;

			for (int I = 0, ICnt = groups == null ? 0 : groups.Length; I < ICnt; I++)
			{
				var group = groups[I];

				var bg = ParseButtonGroup(group);

				bt.Items.Add(bg);
			}

			bt.SetParent();

			return bt;
		}

		/// <summary>
		/// Parse buttons group
		/// </summary>
		public virtual MetaFormButtonGroup ParseButtonGroup(ButtonGroup buttonGroup)
		{
			var bg = CreateButtonGroup(buttonGroup.Title, buttonGroup.Name, buttonGroup.Template.ToString().SToE(MetaButtonGroupTemplate.None));

			bg.SetSecurity(buttonGroup.Security);
			bg.Condition = buttonGroup.Condition;

			var buttons = buttonGroup.Items;

			for (int I = 0, ICnt = buttons == null ? 0 : buttons.Length; I < ICnt; I++)
			{
				var item = buttons[I];

				if (item is Button)
				{
					var b = ParseButton((Button)item);

					bg.Items.Add(b);
				}
				else
				{
					var mctrl = ParseXml(item) as MetaFormButton;

					if (mctrl == null)
						throw new NotSupportedException("ParseButtonGroup {" + item.GetType().FullName + "}");

					bg.Items.Add(mctrl);
				}
			}

			bg.SetParent();

			return bg;
		}

		/// <summary>
		/// Parse button
		/// </summary>
		public virtual MetaFormButton ParseButton(Button button)
		{
			var b = CreateButton(button.Title, button.Name, button.Command);

			b.SetSecurity(button.Security);

			b.Condition = button.Condition;

			b.UseStandardImage = button.UseStandardImage;
			b.ImageUrl16 = button.ImageUrl16;
			b.ImageUrl32 = button.ImageUrl32;
			b.ImageX = button.ImageX;
			b.ImageY = button.ImageY;
			b.ImageLang = button.ImageLangSpecified ? button.ImageLang : 0;
			b.CommandEnable = button.CommandEnable;
			b.Enabled = button.Enabled || !button.EnabledSpecified;

			if (b.UseStandardImage)
			{
				b.ImageUrl16 = null;
				b.ImageUrl32 = null;
			}

			if (b.ImageLang <= 0)
				b.ImageLang = Ctx.Service.Formatter.GetCurrentLCID();

			if (b.CommandEnable.IsNullOrEmpty())
				b.CommandEnable = null;

			if (b.Command.IsNullOrEmpty())
				b.Command = null;

			return b;
		}

		/// <summary>
		/// Parse tabs
		/// </summary>
		public virtual MetaFormTabs ParseTabs(Tabs tabs)
		{
			var mts = CreateTabs(tabs.Title, tabs.Name);

			mts.Enabled = tabs.Enabled || !tabs.EnabledSpecified;
			mts.Visible = tabs.Visible || !tabs.VisibleSpecified;

			mts.Width = tabs.Width.IsNullOrEmpty() ? Unit.Empty : Unit.Parse(tabs.Width);
			mts.Height = tabs.Height.IsNullOrEmpty() ? Unit.Empty : Unit.Parse(tabs.Height);

			for (var i = 0; tabs.Items != null && i < tabs.Items.Length; i++)
			{
				var tab = tabs.Items[i];

				mts.Items.Add(ParseTab(tab));
			}

			mts.SetParent();

			return mts;
		}

		/// <summary>
		/// Parse tab
		/// </summary>
		public virtual MetaFormTab ParseTab(Tab tab)
		{
			var mt = CreateTab(tab.Title, tab.Name);

			mt.Condition = tab.Condition;

			mt.Enabled = tab.Enabled || !tab.EnabledSpecified;
			mt.Visible = tab.Visible || !tab.VisibleSpecified;

			mt.Width = tab.Width.IsNullOrEmpty() ? Unit.Empty : Unit.Parse(tab.Width);
			mt.Height = tab.Height.IsNullOrEmpty() ? Unit.Empty : Unit.Parse(tab.Height);

			for (var i = 0; tab.Items != null && i < tab.Items.Length; i++)
			{
				var item = tab.Items[i];

				if (item is Group)
				{
					var mg = ParseGroup((Group)item);

					mt.Items.Add(mg);
				}
				else if (item is Table)
				{
					var mtbl = ParseTable((Table)item);

					mt.Items.Add(mtbl);
				}
				else if (item is Field)
				{
					var field = (Field)item;

					var fc = CreateFieldCtrl(field.Title, field.Name, field.DisplayTemplateName, field.EditTemplateName);

					if (fc == null)
						continue;

					fc.Enabled = field.Enabled || !field.EnabledSpecified;
					fc.Visible = field.Visible || !field.VisibleSpecified;

					fc.Width = field.Width.IsNullOrEmpty() ? Unit.Empty : Unit.Parse(field.Width);
					fc.Height = field.Height.IsNullOrEmpty() ? Unit.Empty : Unit.Parse(field.Height);

					fc.Condition = field.Condition;

					mt.Items.Add(fc);
				}
				else if (item is Control)
				{
					var cc = ParseControl((Control)item);

					mt.Items.Add(cc);
				}
				else if (item is Label)
				{
					var label = (Label)item;

					var lc = CreateLabelCtrl(label.Title, label.Name);

					lc.Enabled = label.Enabled || !label.EnabledSpecified;
					lc.Visible = label.Visible || !label.VisibleSpecified;

					lc.Width = label.Width.IsNullOrEmpty() ? Unit.Empty : Unit.Parse(label.Width);
					lc.Height = label.Height.IsNullOrEmpty() ? Unit.Empty : Unit.Parse(label.Height);

					mt.Items.Add(lc);
				}
				else
				{
					var mctrl = ParseXml(item);

					if (mctrl == null)
						throw new NotSupportedException("ParseTab {" + item.GetType().FullName + "}");

					mt.Items.Add(mctrl);
				}
			}

			mt.SetParent();

			return mt;
		}

		/// <summary>
		/// Parse group
		/// </summary>
		public virtual MetaFormGroup ParseGroup(Group group)
		{
			var mg = CreateGroup(group.Title, group.Name);

			mg.Condition = group.Condition;

			mg.Enabled = group.Enabled || !group.EnabledSpecified;
			mg.Visible = group.Visible || !group.VisibleSpecified;

			mg.Width = group.Width.IsNullOrEmpty() ? Unit.Empty : Unit.Parse(group.Width);
			mg.Height = group.Height.IsNullOrEmpty() ? Unit.Empty : Unit.Parse(group.Height);

			for (var i = 0; group.Items != null && i < group.Items.Length; i++)
			{
				var item = group.Items[i];

				if (item is Table)
				{
					mg.Items.Add(ParseTable((Table)item));
				}
				else if (item is Field)
				{
					var field = (Field)item;

					var fc = CreateFieldCtrl(field.Title, field.Name, field.DisplayTemplateName, field.EditTemplateName);

					if (fc == null)
						continue;

					fc.Enabled = field.Enabled || !field.EnabledSpecified;
					fc.Visible = field.Visible || !field.VisibleSpecified;

					fc.Width = field.Width.IsNullOrEmpty() ? Unit.Empty : Unit.Parse(field.Width);
					fc.Height = field.Height.IsNullOrEmpty() ? Unit.Empty : Unit.Parse(field.Height);

					fc.Condition = field.Condition;

					mg.Items.Add(fc);
				}
				else if (item is Control)
				{
					var cc = ParseControl((Control)item);

					mg.Items.Add(cc);
				}
				else if (item is Label)
				{
					var label = (Label)item;

					var lc = CreateLabelCtrl(label.Title, label.Name);

					lc.Enabled = label.Enabled || !label.EnabledSpecified;
					lc.Visible = label.Visible || !label.VisibleSpecified;

					lc.Width = label.Width.IsNullOrEmpty() ? Unit.Empty : Unit.Parse(label.Width);
					lc.Height = label.Height.IsNullOrEmpty() ? Unit.Empty : Unit.Parse(label.Height);

					mg.Items.Add(lc);
				}
				else
				{
					var mctrl = ParseXml(item);

					if (mctrl == null)
						throw new NotSupportedException("ParseGroup {" + item.GetType().FullName + "}");

					mg.Items.Add(mctrl);
				}
			}

			mg.SetParent();

			return mg;
		}

		/// <summary>
		/// Parse table
		/// </summary>
		public virtual MetaFormTable ParseTable(Table table)
		{
			var mt = CreateTable(table.Title, table.Name);

			mt.ShowBorders = table.ShowBorders || !table.ShowBordersSpecified;

			mt.Enabled = table.Enabled || !table.EnabledSpecified;
			mt.Visible = table.Visible || !table.VisibleSpecified;

			mt.Width = table.Width.IsNullOrEmpty() ? Unit.Empty : Unit.Parse(table.Width);
			mt.Height = table.Height.IsNullOrEmpty() ? Unit.Empty : Unit.Parse(table.Height);

			for (var rowIdx = 0; table.Items != null && rowIdx < table.Items.Length; rowIdx++)
			{
				var row = table.Items[rowIdx];

				var mr = CreateTableRow(row.Title, row.Name);

				mr.Enabled = row.Enabled || !row.EnabledSpecified;
				mr.Visible = row.Visible || !row.VisibleSpecified;

				mr.Width = row.Width.IsNullOrEmpty() ? Unit.Empty : Unit.Parse(row.Width);
				mr.Height = row.Height.IsNullOrEmpty() ? Unit.Empty : Unit.Parse(row.Height);

				mt.Items.Add(mr);

				for (var cellIdx = 0; row.Items != null && cellIdx < row.Items.Length; cellIdx++)
				{
					var cell = row.Items[cellIdx];

					var mc = CreateTableCell(cell.Title, cell.Name);

					mc.Enabled = cell.Enabled || !cell.EnabledSpecified;
					mc.Visible = cell.Visible || !cell.VisibleSpecified;

					mc.Width = cell.Width.IsNullOrEmpty() ? Unit.Empty : Unit.Parse(cell.Width);
					mc.Height = cell.Height.IsNullOrEmpty() ? Unit.Empty : Unit.Parse(cell.Height);

					mr.Items.Add(mc);

					for (var fieldIdx = 0; cell.Items != null && fieldIdx < cell.Items.Length; fieldIdx++)
					{
						var item = cell.Items[fieldIdx];

						if (item is Field)
						{
							var field = (Field)item;

							var fc = CreateFieldCtrl(field.Title, field.Name, field.DisplayTemplateName, field.EditTemplateName);

							if (fc == null)
								continue;

							fc.Enabled = field.Enabled || !field.EnabledSpecified;
							fc.Visible = field.Visible || !field.VisibleSpecified;

							fc.Width = field.Width.IsNullOrEmpty() ? Unit.Empty : Unit.Parse(field.Width);
							fc.Height = field.Height.IsNullOrEmpty() ? Unit.Empty : Unit.Parse(field.Height);

							fc.Condition = field.Condition;

							mc.Items.Add(fc);
						}
						else if (item is Control)
						{
							var cc = ParseControl((Control)item);

							mc.Items.Add(cc);
						}
						else if (item is Label)
						{
							var label = (Label)item;

							var lc = CreateLabelCtrl(label.Title, label.Name);

							lc.Enabled = label.Enabled || !label.EnabledSpecified;
							lc.Visible = label.Visible || !label.VisibleSpecified;

							lc.Width = label.Width.IsNullOrEmpty() ? Unit.Empty : Unit.Parse(label.Width);
							lc.Height = label.Height.IsNullOrEmpty() ? Unit.Empty : Unit.Parse(label.Height);

							mc.Items.Add(lc);
						}
						else if (item is Group)
						{
							mc.Items.Add(ParseGroup((Group)item));
						}
						else if (item is Table)
						{
							mc.Items.Add(ParseTable((Table)item));
						}
						else
						{
							var mctrl = ParseXml(item);

							if (mctrl == null)
								throw new NotSupportedException("ParseTable {" + item.GetType().FullName + "}");

							mc.Items.Add(mctrl);
						}
					}

					mc.SetParent();
				}

				mr.SetParent();
			}

			mt.SetParent();

			return mt;
		}

		/// <summary>
		/// Parse control
		/// </summary>
		public virtual MetaFormControl ParseControl(Control control)
		{
			var res = CreateControl();

			res.WebControl = control.WebControl;
			res.UserControl = control.UserControl;
			res.IsField = control.IsFieldCtrl && control.IsFieldCtrlSpecified;

			return res;
		}

		/// <summary>
		/// Parse <paramref name="scripts"/>
		/// </summary>
		private MetaFormScripts ParseScripts(Scripts scripts)
		{
			var mss = CreateScripts(scripts.Name);

			for (var i = 0; scripts.Items != null && i < scripts.Items.Length; i++)
			{
				var script = scripts.Items[i];

				mss.Items.Add(ParseScript(script));
			}

			mss.SetParent();

			return mss;
		}

		/// <summary>
		/// Parse <paramref name="script"/>
		/// </summary>
		private MetaFormScript ParseScript(Script script)
		{
			var displacement =
				script.DisplacementSpecified && script.Displacement == ScriptDisplacement.Head
					? MetaFormScriptDisplacement.Head
					: MetaFormScriptDisplacement.Body;

			var s = CreateScript(script.Name, script.Src, script.Text, displacement);

			s.SetSecurity(script.Security);

			s.Condition = script.Condition;

			return s;
		}

		/// <summary>
		/// Create forms
		/// </summary>
		public virtual MetaForms CreateForms()
		{
			return new MetaForms();
		}

		/// <summary>
		/// Create choose rule
		/// </summary>
		public virtual MetaFormsRule CreateChooseRule(string condition, string formName)
		{
			return new MetaFormsRule(condition, formName);
		}

		/// <summary>
		/// Create form
		/// </summary>
		public virtual MetaForm CreateForm(string title, string name)
		{
			return new MetaForm(title, name);
		}

		/// <summary>
		/// Create buttons tabs
		/// </summary>
		public virtual MetaFormButtonTabs CreateButtonTabs()
		{
			return new MetaFormButtonTabs();
		}

		/// <summary>
		/// Create buttons tab
		/// </summary>
		public virtual MetaFormButtonTab CreateButtonTab(string title, string name, bool initial)
		{
			return new MetaFormButtonTab(title, name, initial);
		}

		/// <summary>
		/// Create buttons group
		/// </summary>
		public virtual MetaFormButtonGroup CreateButtonGroup(string title, string name, MetaButtonGroupTemplate template)
		{
			return new MetaFormButtonGroup(title, name, template);
		}

		/// <summary>
		/// Create button
		/// </summary>
		public virtual MetaFormButton CreateButton(string title, string name, string command)
		{
			return new MetaFormButton(title, name, command);
		}

		/// <summary>
		/// Create tabs
		/// </summary>
		public virtual MetaFormTabs CreateTabs(string title, string name)
		{
			return new MetaFormTabs(title, name);
		}

		/// <summary>
		/// Create tab
		/// </summary>
		public virtual MetaFormTab CreateTab(string title, string name)
		{
			return new MetaFormTab(title, name);
		}

		/// <summary>
		/// Create group
		/// </summary>
		public virtual MetaFormGroup CreateGroup(string title, string name)
		{
			return new MetaFormGroup(title, name);
		}

		/// <summary>
		/// Create table
		/// </summary>
		public virtual MetaFormTable CreateTable(string title, string name)
		{
			return new MetaFormTable(title, name);
		}

		/// <summary>
		/// Create table row
		/// </summary>
		public virtual MetaFormTableRow CreateTableRow(string title, string name)
		{
			return new MetaFormTableRow(title, name);
		}

		/// <summary>
		/// Create table cell
		/// </summary>
		public virtual MetaFormTableCell CreateTableCell(string title, string name)
		{
			return new MetaFormTableCell(title, name);
		}

		/// <summary>
		/// Create field
		/// </summary>
		public virtual MetaFormFieldCtrl CreateFieldCtrl(string title, string name, string displayTemplateName, string editTemplateName)
		{
			var meta = Ctx.Meta.Field(name);

			if (meta == null)
				return null;

			var res = new MetaFormFieldCtrl(meta);

			if (!title.IsNullOrEmpty())
				res.Title = title;

			if (!displayTemplateName.IsNullOrEmpty())
				res.DisplayTemplateName = displayTemplateName;

			if (!editTemplateName.IsNullOrEmpty())
				res.EditTemplateName = editTemplateName;

			return res;
		}

		/// <summary>
		/// Create label
		/// </summary>
		public virtual MetaFormLabelCtrl CreateLabelCtrl(string title, string name)
		{
			var res = new MetaFormLabelCtrl(title, name);

			return res;
		}

		/// <summary>
		/// Create Control
		/// </summary>
		public virtual MetaFormControl CreateControl()
		{
			var res = new MetaFormControl();

			return res;
		}

		/// <summary>
		/// Create action rule
		/// </summary>
		public virtual MetaFormRule CreateActionRule(string condition, string action)
		{
			return new MetaFormRule(condition, action);
		}

		/// <summary>
		/// Create scripts
		/// </summary>
		public virtual MetaFormScripts CreateScripts(string name)
		{
			return new MetaFormScripts(name);
		}

		/// <summary>
		/// Create script
		/// </summary>
		public virtual MetaFormScript CreateScript(string name, string src, string text, MetaFormScriptDisplacement displacement)
		{
			return new MetaFormScript(name, src, text, displacement);
		}

		/// <summary>
		/// Parse event info
		/// </summary>
		public class ParseEventInfo
		{
			/// <summary>
			/// Context
			/// </summary>
			public FormsContext Ctx;

			/// <summary>
			/// Builder
			/// </summary>
			public XmlFormsBuilder Builder;

			/// <summary>
			/// Xml element
			/// </summary>
			public object Xml;

			/// <summary>
			/// Metadata (result)
			/// </summary>
			public MetaFormCtrl Meta;
		}

		/// <summary>
		/// Parse unknown xml
		/// </summary>
		public virtual MetaFormCtrl ParseXml(object xml)
		{
			var em = SPServices.EventManager;

			var info = new ParseEventInfo
						{
							Ctx = Ctx,
							Builder = this,
							Xml = xml,
							Meta = null
						};

			em.FireEvent(string.Format(Const.EventNames.Forms.ParseXml, Ctx.ServiceName), info);

			return info.Meta;
		}
	}
}